package org.wdm.parsers;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.wdm.beans.Route;
import org.wdm.beans.Step;
import org.wdm.beans.points.Point;

/**
 *
 * @author iakopap
 */
public class GoogleParser {

    public static List<Route> parse(String from, String to, String alternatives, String mode) {

        //Building the request url for the google directions service
        StringBuilder sb = new StringBuilder("http://maps.googleapis.com/maps/api/directions/json?");
        sb.append("origin=").append(from).append("&");
        sb.append("destination=").append(to).append("&");
        sb.append("sensor=").append("false").append("&");
        sb.append("alternatives=").append(alternatives != null ? "true" : "false").append("&");
        sb.append("mode=").append(mode);
//        System.out.println(sb.toString().replaceAll(" ", "%20"));

        //Sending a request to google directions service
        StringBuilder json = new StringBuilder();
        try {
            //Encoding the request url
            URL url = new URL(sb.toString().replaceAll(" ", "%20"));

            //Reading the result of the request
            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
            String line = "";
            while ((line = in.readLine()) != null) {
                json.append(line);
            }
            in.close();
        } catch (IOException e) {
        }

        //Deserializing json into java objects using parser
        JsonParser parser = new JsonParser();

        //Getting the root element
        JsonObject root = parser.parse(json.toString()).getAsJsonObject();

        //Getting the status element
        JsonElement status = root.get("status");
//        System.out.println("Status: " + status);

        //Storing all the available routes in a list
        List<Route> list = new ArrayList<Route>();

        //Getting all the available routes elements, if alternatives enabled
        JsonArray routes = root.get("routes").getAsJsonArray();
        Iterator<JsonElement> it = routes.iterator();
        while (it != null && it.hasNext()) {
            //Creating a new route bean
            Route bean = new Route();

            //Reading the next route element
            JsonObject route = it.next().getAsJsonObject();

            //Setting up the summary of the route bean
            bean.setSummary(route.get("summary").getAsString());

            //Setting up the bound's north-east and south-west points of the route area
            JsonObject northeast = route.get("bounds").getAsJsonObject().get("northeast").getAsJsonObject();
            double neLat = northeast.get("lat").getAsDouble();
            double neLng = northeast.get("lng").getAsDouble();
            bean.setNortheastLatitude(neLat);
            bean.setNortheastLongitude(neLng);
            JsonObject southwest = route.get("bounds").getAsJsonObject().get("southwest").getAsJsonObject();
            double swLat = southwest.get("lat").getAsDouble();
            double swLng = southwest.get("lng").getAsDouble();
            bean.setSouthwestLatitude(swLat);
            bean.setSouthwestLongitude(swLng);

            //Geeting all the leg elements along the route path, if waypoints enabled
            JsonArray legs = route.get("legs").getAsJsonArray();
            Iterator<JsonElement> lit = legs.iterator();
            int distance = 0;
            int duration = 0;
            while (lit != null && lit.hasNext()) {
                //Getting the next leg of the route
                JsonObject leg = lit.next().getAsJsonObject();

                //Calculating the total distance of the route
                distance += leg.get("distance").getAsJsonObject().get("value").getAsInt();

                //Calculating the total duration of the route
                duration += leg.get("duration").getAsJsonObject().get("value").getAsInt();

                //Getting the step elements within this leg along the route path
                JsonArray steps = leg.get("steps").getAsJsonArray();
                Iterator<JsonElement> sit = steps.iterator();

                //Keep adding the rest steps into the route path
                while (sit != null && sit.hasNext()) {
                    //getting the next step element of the leg
                    JsonObject step = sit.next().getAsJsonObject();

                    //Getting the start location of this step, avoiding any duplicate
                    double startlatitude = step.get("start_location").getAsJsonObject().get("lat").getAsDouble();
                    double startlongitude = step.get("start_location").getAsJsonObject().get("lng").getAsDouble();
                    Point startPoint = new Point(startlatitude, startlongitude);

                    //Getting the end location of this step, avoiding any duplicate
                    double endlatitude = step.get("end_location").getAsJsonObject().get("lat").getAsDouble();
                    double endlongitude = step.get("end_location").getAsJsonObject().get("lng").getAsDouble();
                    Point endPoint = new Point(endlatitude, endlongitude);

                    //Getting the instructions message
                    String instructions = step.get("html_instructions").getAsString();

                    //Getting the distance between the next step
                    int stepDistance = step.get("distance").getAsJsonObject().get("value").getAsInt();

                    //Getting the duration between the next step
                    int stepDuration = step.get("duration").getAsJsonObject().get("value").getAsInt();

                    //Getting the travel mode
                    String travelMode = step.get("travel_mode").getAsString();

                    //Adding the next unique step into the route path
                    Step stepBean = new Step(travelMode, stepDistance, "meters", stepDuration, instructions, startPoint, endPoint);
                    bean.addStep(stepBean);
                }
            }

            //Setting up the total distance of the path within route
            bean.setDistance(distance,"meters");

            //Setting up the estimated duration of the route
            bean.setDuration(duration);

            //Printing bean route message
//            System.out.println(bean);

            //Adding the route bean into the list
            list.add(bean);
        }

        return list;
    }
}
